/*
 * Socket.cpp
 *
 *  Created on: 2012-12-7
 *      Author: yangentao@gmail.com
 */

#include "Socket.h"
#include <assert.h>

#include "../Log.h"

namespace yet {



Socket::Socket() {
	sock = INVALID_SOCKET;
}
Socket::Socket(SOCKET s) {
	sock = s;
}

Socket::~Socket() {
}
void Socket::close() {
	if (isValidSocket()) {
		closesocket(sock);
		sock = INVALID_SOCKET;
	}
}
InetAddr Socket::getSockName() {
	InetAddr addr;
	int len = 0;
	int n = getsockname(sock, addr.getAddr(), &len);
	throw_socket_error_if(n != 0);
	return addr;
}

InetAddr Socket::getPeerName() {
	InetAddr addr;
	int len = 0;
	int n = getpeername(sock, addr.getAddr(), &len);
	throw_socket_error_if(n != 0);
	return addr;
}

bool Socket::createTcpSocket() {
	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	return INVALID_SOCKET != sock;
}
bool Socket::createUdpSocket() {
	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	return INVALID_SOCKET != sock;
}

bool Socket::isValidSocket() {
	return INVALID_SOCKET != sock;
}

void Socket::listen() {
	listen(SOMAXCONN);
}
void Socket::listen(int backlog) {
	int n = ::listen(sock, backlog);
	throw_socket_error_if(n != 0);
}

bool Socket::bind(InetAddr addr) {
	int n = ::bind(sock, addr.getAddr(), addr.getSize());
	throw_socket_error_if(n !=0);
	return true;
}
bool Socket::bind() {
	InetAddr addr(0);
	return bind(addr);
}

bool Socket::bind(short localPort) {
	InetAddr addr(localPort);
	return bind(addr);
}
bool Socket::bind(string localIp, short localPort) {
	InetAddr addr(localIp, localPort);
	return bind(addr);
}

SOCKET Socket::accept() {
	return accept(0);
}
SOCKET Socket::accept(InetAddr* outAddr) {
	int size = 0;
	SOCKET s = ::accept(sock, outAddr ? outAddr->getAddr() : 0, outAddr ? &size : 0);
	throw_socket_error_if(s == INVALID_SOCKET);
	return s;
}
bool Socket::connect(InetAddr peerAddr) {
	assert(isValidSocket());
	int n = ::connect(sock, peerAddr.getAddr(), peerAddr.getSize());
	throw_socket_error_if(n != 0);
	return true;
}
bool Socket::connect(string ip, short port) {
	assert(!ip.empty());
	assert(port>0);
	return connect(InetAddr(ip, port));
}
int Socket::send(const char* buffer, int size, int flag) {
	assert(isValidSocket());
	assert(size >=0);
	SocketError::clearError();
	int n = ::send(sock, buffer, size, flag);
	throw_socket_error_if(n < 0);
	return n;
}
int Socket::send(const char* buffer, int size) {
	return send(buffer, size, 0);
}
int Socket::sendOOB(const char* buffer, int size) {
	return send(buffer, size, MSG_OOB);
}
bool Socket::hasMoreData() {
	SocketError e;
	return e.getCode() == WSAEMSGSIZE;
}
int Socket::recv(char* buffer, int size, int flag) {
	SocketError::clearError();
	int n = ::recv(sock, buffer, size, flag);
	throw_socket_error_if(n < 0);
	return n; //n ==0, peer socket closed
}
int Socket::recv(char* buffer, int size) {
	return recv(buffer, size, 0);
}
int Socket::recvOOB(char* buffer, int size) {
	return recv(buffer, size, MSG_OOB);
}
int Socket::sendTo(InetAddr peerAddr, const char* buf, int len) {
	int n = ::sendto(sock, buf, len, 0, peerAddr.getAddr(), peerAddr.getSize());
	throw_socket_error_if(n < 0);
	return n;
}

int Socket::recvFrom(InetAddr* outPeerAddr, char* buf, int len) {
	int size = 0;
	int n = recvfrom(sock, buf, len, 0, outPeerAddr ? outPeerAddr->getAddr() : 0, outPeerAddr ? &size : 0);
	throw_socket_error_if(n < 0);
	return n;
}

bool Socket::selectRead() {
	FdSet fs(sock);
	if (select(&fs, 0, 0, 0)) {
		return fs.isSet(sock);
	}
	return false;
}
bool Socket::selectWrit() {
	FdSet fs(sock);
	if (select(0, &fs, 0, 0)) {
		return fs.isSet(sock);
	}
	return false;
}
bool Socket::selectExcept() {
	FdSet fs(sock);
	if (select(0, 0, &fs, 0)) {
		return fs.isSet(sock);
	}
	return false;
}
bool Socket::select(FdSet* rSet, FdSet* wSet, FdSet* eSet, int seconds, int usec) {
	struct timeval t;
	t.tv_sec = seconds;
	t.tv_usec = usec;
	int n = ::select(0, rSet ? &rSet->fdset : 0, wSet ? &wSet->fdset : 0, eSet ? &eSet->fdset : 0, &t);
	throw_socket_error_if(n ==SOCKET_ERROR);
	if (n == 0) { //time out
		return false;
	}
	return true;
}
void Socket::ioctl(long cmd, u_long* arg) {
	u_long noblock = 1;
	int n = ioctlsocket(sock, FIONBIO, &noblock);
	throw_socket_error_if(n!=0);
}
void Socket::ioctlNoBlock(BOOL noBlock) {
	u_long noblock = noBlock;
	ioctl(FIONBIO, &noblock);
}
u_long Socket::ioctlRead() {
	u_long n = 0;
	ioctl(FIONREAD, &n);
	return n;
}



} /* namespace yet */
